//==========================================================================;
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//  Copyright (c) Microsoft Corporation.  All Rights Reserved.
//
//==========================================================================;

#ifndef __HWSIM_H__
#define __HWSIM_H__


/**************************************************************************
    File    : hwsim.h
    Abstract:
        This file contains the hardware simulation.  It fakes "DMA" transfers,
        scatter gather mapping handling, ISR's, etc...  The ISR routine in
        here will be called when an ISR would be generated by the fake hardware
        and it will directly call into the device level ISR for more accurate
        simulation.
**************************************************************************/
//
// SCATTER_GATHER_MAPPINGS_MAX:
//
// The maximum number of entries in the hardware's scatter/gather list.  I
// am making this so large for a few reasons:
//
//     1) we're faking this with uncompressed surfaces -- 
//            these are large buffers which will map to a lot of s/g entries
//     2) the fake hardware implementation requires at least one frame's
//            worth of s/g entries to generate a frame
//
#define SCATTER_GATHER_MAPPINGS_MAX 128

//
// SCATTER_GATHER_ENTRY:
//
// This structure is used to keep the scatter gather table for the fake
// hardware as a doubly linked list.
//
typedef struct _SCATTER_GATHER_ENTRY {

    LIST_ENTRY ListEntry;
    PUCHAR Virtual;
    ULONG ByteCount;

} SCATTER_GATHER_ENTRY, *PSCATTER_GATHER_ENTRY;

//
// CHardwareSimulation:
//
// The hardware simulation class.
//
class CHardwareSimulation {

private:

    ULONG m_BufferRemaining;
  
    //
    // The transport stream synthesizer.  This is a piece of code which
    // fills in the requested transport stream.
    //
    CTsSynthesizer *m_TsSynth;

    //
    // The synthesis buffer.  This is a private buffer we use to generate the
    // transport stream in.  The fake "scatter / gather" mappings are filled
    // in from this buffer during each interrupt.
    //
    PUCHAR m_SynthesisBuffer;

    //
    // Key information regarding the frames we generate.
    //
    LONGLONG m_TimePerFrame;
    ULONG m_PacketSize;
    ULONG m_PacketsPerSample;
    ULONG m_SampleSize;


    //
    // Scatter gather mappings for the simulated hardware.
    //
    KSPIN_LOCK m_ListLock;
    LIST_ENTRY m_ScatterGatherMappings;

    //
    // Lookaside for memory for the scatter / gather entries on the scatter /
    // gather list.
    //
    NPAGED_LOOKASIDE_LIST m_ScatterGatherLookaside;

    //
    // The current state of the fake hardware.
    //
    HARDWARE_STATE m_HardwareState;

    //
    // The pause / stop hardware flag and event.
    //
    BOOLEAN m_StopHardware;
    KEVENT m_HardwareEvent;

    //
    // Maximum number of scatter / gather mappins in the s/g table of the
    // fake hardware.
    //
    ULONG m_ScatterGatherMappingsMax;

    //
    // Number of scatter / gather mappings that have been completed (total)
    // since the start of the hardware or any reset.
    //
    ULONG m_NumMappingsCompleted;

    //
    // Number of scatter / gather mappings that are queued for this hardware.
    //
    ULONG m_ScatterGatherMappingsQueued;
    ULONG m_ScatterGatherBytesQueued;

    //
    // Number of frames skipped due to lack of scatter / gather mappings.
    //
    ULONG m_NumFramesSkipped;

    //
    // The "Interrupt Time".  Number of "fake" interrupts that have occurred
    // since the hardware was started.
    // 
    ULONG m_InterruptTime;

    //
    // The system time at start.
    //
    LARGE_INTEGER m_StartTime;
    LARGE_INTEGER m_LastTimerTime;
    
    //
    // The DPC used to "fake" ISR
    //
    KDPC m_IsrFakeDpc;
    KTIMER m_IsrTimer;

    //
    // The hardware sink that will be used for interrupt notifications.
    //
    IHardwareSink *m_HardwareSink;

    //
    // PCR_PID of current TS.
    // Use only one PCR if TS includes multiple programs.
    //
    WORD m_wPcrPid;

    //
    // Last PCR value; to analyze clock gap.
    // Offset PCR; to generate ideal current clock.
    //
    LONGLONG m_llLastPcr;
    LONGLONG m_llPcrOffset;

    //
    // The current TSID
    //
    LONG m_lCurrTSID;


    
    //
    // FakeHardware():
    //
    // Called from the simulated interrupt.  First we fake the hardware's
    // actions (at DPC) then we call the "Interrupt service routine" on
    // the hardware sink.
    //
    void 
    FakeHardware (
        );

    //
    // SimulatedInterrupt():
    //
    // This is the hardware's simulated interrupt.  Really, it's just a DPC.
    // We'll use a spinlock instead of any KeSynchronizeExecutions.
    //
    static
    void
    SimulatedInterrupt (
        IN PKDPC Dpc,
        IN CHardwareSimulation *HardwareSim,
        IN PVOID SystemArg1,
        IN PVOID SystemArg2
        )
    {
    	 UNREFERENCED_PARAMETER(Dpc);
    	 UNREFERENCED_PARAMETER(SystemArg1);
    	 UNREFERENCED_PARAMETER(SystemArg2);
		
        HardwareSim -> FakeHardware ();
    }

    //
    // FillScatterGatherBuffers():
    //
    // This is called by the hardware simulation to fill a series of scatter /
    // gather buffers with synthesized data.
    //
    NTSTATUS
    FillScatterGatherBuffers (
        LONGLONG * pllPcr
        );

    //
    // GetTsLocation():
    //
    // Wrapper of CTsSynthesizer::GetTsLocation().
    //
    PUCHAR
    GetTsLocation (
        PUCHAR Buffer
        );

    //
    // GetLastPcr():
    //
    // Get the latest PCR value in buffer.
    //
    LONGLONG
    GetLastPcr (
        PUCHAR  pBuffer,
        ULONG   ulBytes
        );

public:

    //
    // CHardwareSimulation():
    //
    // The hardware simulation constructor.  Since the new operator will
    // have zeroed the memory, only initialize non-NULL, non-0 fields. 
    //
    CHardwareSimulation (
        IN IHardwareSink *HardwareSink
        );

    //
    // ~CHardwareSimulation():
    //
    // The hardware simulation destructor.
    //
    ~CHardwareSimulation (
        )
    {
    }        

    // CleanupHW()
    //
    // Cleanup the hwsimulation object associated with this device.  This is the
    // free callback for the bagged hwsim.
    //
    static
    void
    CleanupHW(
          IN CHardwareSimulation *hwSim
          ) 
    {
        delete hwSim;
    }

    //
    // Start():
    //
    // "Start" the fake hardware.  This will start issuing interrupts and 
    // DPC's. 
    //
    // The sample rate, sample size, and a synthesizer must be provided.
    //
    NTSTATUS
    Start (
        CTsSynthesizer *TsSynth,
        IN LONGLONG TimePerSample,
        IN ULONG PacketWidth,
        IN ULONG PacketsPerSample
        );

    //
    // Pause():
    //
    // "Pause" or "unpause" the fake hardware.  This will stop issuing 
    // interrupts or DPC's on a pause and restart them on an unpause.  Note
    // that this will not reset counters as a Stop() would.
    //
    NTSTATUS
    Pause (
        IN BOOLEAN Pausing
        );

    //
    // Stop():
    //
    // "Stop" the fake hardware.  This will stop issuing interrupts and
    // DPC's.
    //
    NTSTATUS
    Stop (
        );

    //
    // ProgramScatterGatherMappings():
    //
    // Program a series of scatter gather mappings into the fake hardware.
    //
    ULONG
    ProgramScatterGatherMappings (
        IN PUCHAR *Buffer,
        IN PKSMAPPING Mappings,
        IN ULONG MappingsCount,
        IN ULONG MappingStride
        );



    PKSPIN_LOCK m_ListLockAddr;


    //
    // ReadNumberOfMappingsCompleted():
    //
    // Read the number of mappings completed since the last hardware reset.
    //
    ULONG
    ReadNumberOfMappingsCompleted (
        );

    //
    // Get the current TSID
    //
    LONG
    GetCurrentTSID() { return m_lCurrTSID; }

};

#endif



